SPB Git forge

spb/ai-atlas

Public
41commits 1branches 0releases
4.6 MBsize
maindefault branch
12 days agolast push
HTML 77.2% TypeScript 10.5% Python 9.6% JavaScript 2.5%
14.0 KB · 258 lines tsx
Raw Blame History
1import type { Metadata } from 'next';2import { ScrollX } from '@/components/models/scroll-x';3import Link from 'next/link';4import { notFound } from 'next/navigation';5import type { ScatterPoint } from '@/components/charts';6import { ParetoChart } from '@/components/benchmarks/client-charts';7import { TrustBadge } from '@/components/models/badges';8import { configChipsOf, fmtScoreUnit, opennessLabel, xFormatter } from '@/components/models/shared';9import { Estimated } from '@/components/ui/badges';10import { DataTable, Td, Th } from '@/components/ui/data-table';11import { Container, Note, PageHeader } from '@/components/ui/section';12import { EmptyState, Unavailable } from '@/components/ui/unavailable';13import { ApiError, apiD1, safe } from '@/lib/api';14import { cn } from '@/lib/cn';15import { fmtInt } from '@/lib/format';16import { routes, SITE_NAME, SITE_URL } from '@/lib/site';17import type { ParetoPayload } from '@/lib/types';1819/*20  Cost vs performance for one benchmark (Pareto view from `/pareto`): X = cheapest current output price (log toggle) or another axis,21  Y = score in the comparability group, bubble = context or parameters (joined from a second /pareto call), frontier from the API.22*/2324type SP = Record<string, string | undefined>;25type Params = { params: Promise<{ slug: string }>; searchParams: Promise<SP> };26export const revalidate = 600;27const X_AXES = [28  { key: 'output_price', label: 'Output price' },29  { key: 'input_price', label: 'Input price' },30  { key: 'parameter_count', label: 'Parameters' },31  { key: 'context_length', label: 'Context' },32  { key: 'memory_estimate', label: 'Memory (est.)' },33];3435export async function generateMetadata({ params }: Params): Promise<Metadata> {36  const { slug } = await params;37  const d = await safe(apiD1.benchmark(slug));38  if (!d || d.entity_type !== 'benchmark') return { title: 'Cost vs performance', robots: { index: false } };39  const title = `${d.name} — Cost vs Performance (Pareto)`;40  const description = `Every canonical model with a current ${d.name} result plotted against its cheapest current output price (USD per 1M tokens), with the Pareto frontier — same comparability group only, trust level on every point. ${SITE_NAME}.`;41  const canonical = `${routes.benchmark(d.slug)}/cost-vs-performance`;42  return { title, description, alternates: { canonical }, openGraph: { title: `${title} | ${SITE_NAME}`, description, url: `${SITE_URL}${canonical}`, type: 'article' } };43}4445async function loadPareto(q: Record<string, string | number | undefined>): Promise<{ res: ParetoPayload | null; error: string | null }> {46  try {47    return { res: await apiD1.pareto(q), error: null };48  } catch (e) {49    if (e instanceof ApiError && e.notFound) notFound();50    if (e instanceof ApiError && (e.status === 400 || e.status === 422)) return { res: null, error: e.detail ?? 'Unsupported axis.' };51    return { res: null, error: null };52  }53}5455export default async function CostVsPerformancePage({ params, searchParams }: Params) {56  const { slug } = await params;57  const sp = await searchParams;58  const x = X_AXES.find((a) => a.key === sp.x) ?? X_AXES[0]!;59  const log = sp.log !== '0';60  const bubble = sp.bubble === 'params' ? 'params' : sp.bubble === 'none' ? 'none' : 'context';61  const metric = sp.metric?.trim() || undefined;62  const configKey = sp.config_key?.trim() || undefined;63  const org = sp.org?.trim() || undefined;64  const openness = sp.openness?.trim() || undefined;65  const base = { benchmark: slug, metric, config_key: configKey, org, openness };66  const [detail, { res, error }, bubbleRes] = await Promise.all([safe(apiD1.benchmark(slug)), loadPareto({ ...base, x: x.key }), bubble === 'none' ? Promise.resolve(null) : safe(apiD1.pareto({ ...base, x: bubble === 'params' ? 'parameter_count' : 'context_length' }))]);67  if (!detail || detail.entity_type !== 'benchmark') notFound();68  const canonical = `${routes.benchmark(detail.slug)}/cost-vs-performance`;69  const href = (patch: SP) => {70    const p = new URLSearchParams();71    for (const [k, v] of Object.entries({ x: x.key === 'output_price' ? undefined : x.key, log: log ? undefined : '0', bubble: bubble === 'context' ? undefined : bubble, metric, config_key: configKey, org, openness, ...patch })) if (v) p.set(k, v);72    const s = p.toString();73    return `${canonical}${s ? `?${s}` : ''}`;74  };75  const unit = typeof detail.attributes?.unit === 'string' ? (detail.attributes.unit as string) : null;76  const bubbleById = new Map<string, number>();77  for (const p of bubbleRes?.points ?? []) bubbleById.set(p.model.id, p.x);78  const frontierSet = new Set(res?.frontier ?? []);79  const points: ScatterPoint[] = (res?.points ?? []).map((p) => {80    const chips = configChipsOf(p.config, res?.group?.config ?? null, 3);81    return {82      id: p.id,83      x: p.x,84      y: p.y,85      r: bubble === 'none' ? 1 : bubbleById.get(p.model.id) ?? 1,86      label: p.model.name,87      sub: [p.model.organization, p.provider?.name ? `via ${p.provider.name}` : null, `rank ${p.rank}`, p.trust_level, chips.map((c) => `${c.key}=${c.value}`).join(' ') || null, p.estimated ? 'estimated' : null].filter(Boolean).join(' · '),88      href: `/models/${encodeURIComponent(p.model.slug)}`,89      color: p.model.openness && /open|restricted/.test(p.model.openness) ? 'var(--positive)' : 'var(--type-model)',90      group: p.model.openness ?? undefined,91    };92  });93  const frontierPts = points.filter((p) => frontierSet.has(p.id)).sort((a, b) => a.x - b.x);94  const hib = res?.group?.higher_is_better !== false;95  const yFmt = (v: number) => fmtScoreUnit(v, unit);96  const xFmt = xFormatter(x.key);97  const chip = (on: boolean) => cn('inline-flex h-8 items-center border px-2.5 text-xs whitespace-nowrap', on ? 'border-ink bg-ink text-canvas' : 'border-rule text-ink-2 hover:border-rule-strong hover:text-ink');98  const ld = { '@context': 'https://schema.org', '@type': 'Dataset', name: `${detail.name} — cost vs performance`, url: `${SITE_URL}${canonical}`, description: res?.methodology };99100  return (101    <Container wide>102      <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(ld) }} />103      <PageHeader104        eyebrow={105          <>106            <Link href={routes.benchmarks()} className="hover:text-ink">107              Benchmarks108            </Link>109            <span aria-hidden>/</span>110            <Link href={routes.benchmark(detail.slug)} className="hover:text-ink">111              {detail.name}112            </Link>113          </>114        }115        title={`${detail.name} — cost vs performance`}116        lede={res?.group ? `Best current row per canonical model in the group “${res.group.label}” (${fmtInt(res.group.model_count)} models) against ${res.x.label}. The dashed line is the Pareto frontier: no model is both better and cheaper than a point on it.` : 'Best current row per canonical model against its cheapest current price.'}117        aside={118          <Link href={routes.benchmark(detail.slug)} className="inline-flex h-9 items-center border border-rule px-2.5 text-sm text-ink-2 hover:border-rule-strong hover:text-ink">119            ← Leaderboard120          </Link>121        }122      >123        <div className="mt-5 flex flex-wrap items-center gap-x-6 gap-y-2 text-xs" data-pareto-controls>124          <span className="flex flex-wrap items-center gap-1.5">125            <span className="eyebrow mr-1">X</span>126            {X_AXES.map((a) => (127              <Link key={a.key} href={href({ x: a.key === 'output_price' ? undefined : a.key })} className={chip(a.key === x.key)} aria-current={a.key === x.key ? 'true' : undefined}>128                {a.label}129              </Link>130            ))}131          </span>132          <span className="flex items-center gap-1.5">133            <span className="eyebrow mr-1">Scale</span>134            <Link href={href({ log: undefined })} className={chip(log)} aria-pressed={log}>135              log136            </Link>137            <Link href={href({ log: '0' })} className={chip(!log)} aria-pressed={!log}>138              linear139            </Link>140          </span>141          <span className="flex items-center gap-1.5">142            <span className="eyebrow mr-1">Bubble</span>143            <Link href={href({ bubble: undefined })} className={chip(bubble === 'context')}>144              context145            </Link>146            <Link href={href({ bubble: 'params' })} className={chip(bubble === 'params')}>147              params148            </Link>149            <Link href={href({ bubble: 'none' })} className={chip(bubble === 'none')}>150              none151            </Link>152          </span>153          {res && res.groups.length > 1 && (154            <form action={canonical} method="get" className="flex items-center gap-1.5">155              {x.key !== 'output_price' && <input type="hidden" name="x" value={x.key} />}156              {!log && <input type="hidden" name="log" value="0" />}157              <span className="eyebrow mr-1">Group</span>158              <select name="group" defaultValue={res.group ? `${res.group.metric}|${res.group.config_key}` : ''} className="h-8 max-w-[18rem] border border-rule bg-surface px-2 text-xs text-ink" aria-label="Comparability group">159                {res.groups.map((g) => (160                  <option key={g.config_key} value={`${g.metric}|${g.config_key}`}>161                    {g.label} ({g.model_count})162                  </option>163                ))}164              </select>165              <button type="submit" className="inline-flex h-8 items-center bg-ink px-2.5 text-xs font-medium text-canvas">166                Go167              </button>168            </form>169          )}170        </div>171      </PageHeader>172173      <div className="space-y-8 pb-16">174        {error ? (175          <EmptyState title="This axis is not available">{error}</EmptyState>176        ) : !res ? (177          <Unavailable what="Pareto view" />178        ) : points.length === 0 ? (179          <EmptyState title="No model has both a score in this group and a value on this axis">Try another axis or comparability group.</EmptyState>180        ) : (181          <>182            <section data-pareto-chart>183              <ParetoChart points={points} xKey={x.key} unit={unit} log={log} xLabel={res.x.label} yLabel={res.y.label} frontier={frontierPts.map((p) => ({ x: p.x, y: p.y }))} highlight={[...frontierSet]} />184              <p className="mt-2 flex flex-wrap items-center gap-x-4 gap-y-1 text-[11px] text-ink-3">185                <span className="inline-flex items-center gap-1">186                  <span className="inline-block size-2 rounded-full" style={{ background: 'var(--positive)' }} /> open / restricted weights187                </span>188                <span className="inline-flex items-center gap-1">189                  <span className="inline-block size-2 rounded-full" style={{ background: 'var(--type-model)' }} /> closed190                </span>191                <span className="inline-flex items-center gap-1">192                  <span className="inline-block h-[2px] w-4 border-t border-dashed border-accent-2" /> Pareto frontier ({fmtInt(frontierPts.length)} models)193                </span>194                {bubble !== 'none' && <span>bubble = {bubble === 'params' ? 'total parameters' : 'context window'}{bubbleRes ? '' : ' (unavailable — uniform)'}</span>}195                {x.key === 'memory_estimate' && <Estimated />}196              </p>197            </section>198            <section>199              <p className="eyebrow mb-2">200                Frontier models <span className="tnum text-ink-3">{fmtInt(frontierPts.length)}</span>201              </p>202              <ScrollX><DataTable caption="Pareto frontier" compact>203                <thead>204                  <tr>205                    <Th>Model</Th>206                    <Th num>{res.y.label}</Th>207                    <Th num>Rank</Th>208                    <Th num>{res.x.label}</Th>209                    <Th>Provider</Th>210                    <Th>Trust</Th>211                  </tr>212                </thead>213                <tbody>214                  {(hib ? [...frontierPts].sort((a, b) => b.y - a.y) : [...frontierPts].sort((a, b) => a.y - b.y)).map((p) => {215                    const raw = res.points.find((q) => q.id === p.id)!;216                    return (217                      <tr key={p.id}>218                        <Td primary>219                          <Link href={p.href ?? '#'} className="text-ink hover:text-accent hover:underline">220                            {p.label}221                          </Link>222                          <span className="block text-[11px] text-ink-3">223                            {raw.model.organization ?? ''}224                            {raw.model.openness ? ` · ${opennessLabel(raw.model.openness)}` : ''}225                          </span>226                        </Td>227                        <Td num label={res.y.label} className="tnum font-medium">228                          {yFmt(p.y)}229                        </Td>230                        <Td num label="Rank" className="tnum text-ink-2">231                          {fmtInt(raw.rank)}232                        </Td>233                        <Td num label={res.x.label} className="tnum text-accent-2">234                          {xFmt(p.x)}235                          {raw.estimated && <span className="ml-1 text-[10px] uppercase text-warning">est.</span>}236                        </Td>237                        <Td label="Provider" className="text-ink-2">238                          {raw.provider ? <Link href={routes.entity(raw.provider)} className="hover:text-accent">{raw.provider.name}</Link> : '—'}239                        </Td>240                        <Td label="Trust">241                          <TrustBadge level={raw.trust_level} />242                        </Td>243                      </tr>244                    );245                  })}246                </tbody>247              </DataTable></ScrollX>248            </section>249            <Note>250              <strong className="font-medium text-ink-2">Methodology.</strong> {res.methodology} Only the selected comparability group is plotted; points under other configurations are not mixed in. Price = cheapest current offer across providers at the time of the last crawl. Nothing is estimated except where marked.251            </Note>252          </>253        )}254      </div>255    </Container>256  );257}258